﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace limpalex.bbparser.Helpers
{
    class StringContainer
    {
        private readonly string _data;
        private int _position;
        public StringContainer(string data)
        {
            if ( string.IsNullOrEmpty(data))
                throw new ArgumentNullException("data");
            _data = data;
            _position = 0;
        }

        public bool Ended
        {
            get
            {
                return _position == _data.Length;
            }
        }

        public bool NotEnded
        {
            get
            {
                return !Ended;
            }
        }

        public void Next()
        {
            if ( Ended )
                throw new EndOfStreamException();
            _position++;
        }

        public char Current
        {
            get
            {
                if (Ended)
                    throw new EndOfStreamException();
                return _data[_position];
            }
        }

        public int Position
        {
            get
            {
                return _position;
            }
        }

        public void ToEnd()
        {
            _position = _data.Length;
        }

        public char GetWithOffset(int offset)
        {
            if ( offset < 0 )
                throw new ArgumentOutOfRangeException("offset");
            return _data[_position + offset];
        }

        public void SetPosition(int position)
        {
            if ( position == _data.Length )
                throw new InvalidOperationException("cannot set end of stream position. use 'ToEnd' method instead");
            if (position + 1 > _data.Length)
                throw new ArgumentOutOfRangeException("position");
            this._position = position;
        }

        public char FindClosest(char[] list)
        {
            var obj = new List<char>(list).ConvertAll(ch => new {Char = ch, Index = FindIndex(ch)});
            obj.Sort((o1, o2) => o1.Index.CompareTo(o2.Index));
            return obj[0].Char;
        }

        public string Substring(int index, int length)
        {
            return _data.Substring(index, length);
        }

        public string Substring(int length)
        {
            return _data.Substring(_position, length);
        }

        public string Substring()
        {
            return _data.Substring(_position);
        }

        public Char AfterLettersOrDigits()
        {
            if ( Ended)
                throw new EndOfStreamException();
            for ( int i = _position; i < _data.Length; i++)
            {
                if ( Char.IsLetterOrDigit(_data[i]))
                    continue;
                return _data[i];
            }
            return '\0';
        }

        public string Take(int count)
        {
            if ( count <= 0 )
                throw new ArgumentOutOfRangeException("count");
            string retval = Substring(count);
            if ( Position + count < _data.Length)
                SetPosition(Position + count);
            else
                ToEnd();
            return retval;
        }

        public string TakeUntil(int index)
        {
            if ( index <= Position)
                throw new ArgumentOutOfRangeException("index");
            return Take(index - Position);
        }

        public int FindIndex(char ch)
        {
            return FindIndex(ch, false);
        }

        public int FindIndex(char ch, bool skipDouble)
        {
            if ( Ended )
                throw new EndOfStreamException();
            for ( int i = _position; i < _data.Length; i++)
            {
                if (ch == _data[i])
                {
                    if ( skipDouble)
                    {
                        if ( i < _data.Length -1)
                        {
                            if ( _data[i+1] != ch)
                                return i;
                        }
                    }
                    else
                    {
                        return i;
                    }
                }
            }

            return -1;
        }
    }
}
